package crypto

import (
	"bytes"
	"encoding/hex"

	"crypto/rand"
	"crypto/rc4"

	"golang.org/x/crypto/chacha20"
	"golang.org/x/crypto/chacha20poly1305"
	"golang.org/x/crypto/nacl/auth"
	"golang.org/x/crypto/nacl/box"
	"golang.org/x/crypto/nacl/secretbox"
	"golang.org/x/crypto/nacl/sign"
	"golang.org/x/crypto/salsa20"
)

func NewSc() *sc {
	key := make([]byte, 16)
	nonce := make([]byte, 12) // chacha20.NonceSize chacha20.NonceSizeX
	var nonce2 [24]byte

	_, err := rand.Read(key)
	if err != nil {
		panic(err)
	}
	_, err = rand.Read(nonce)
	if err != nil {
		panic(err)
	}
	_, err = rand.Read(nonce2[:])
	if err != nil {
		panic(err)
	}

	publicKey, privateKey, err := box.GenerateKey(rand.Reader) // NaclBox
	if err != nil {
		panic(err)
	}

	publicKey2, privateKey2, err := sign.GenerateKey(rand.Reader) // NaclSign
	if err != nil {
		panic(err)
	}

	secretKeyBytes, err := hex.DecodeString("6368616e676520746869732070617373776f726420746f206120736563726574")
	if err != nil {
		panic(err)
	}

	var secretKey [32]byte
	copy(secretKey[:], secretKeyBytes)

	return &sc{
		key:            key,
		key2:           [32]byte{},
		nonce:          nonce,
		nonce2:         nonce2,
		secretKey:      &secretKey,
		privateKey:     privateKey, // NaclBox
		publicKey:      publicKey,
		privateKey2:    privateKey2,
		publicKey2:     publicKey2,
		associatedData: []byte("Additional Data"),
	}
}

type sc struct {
	key            []byte
	key2           [32]byte
	nonce          []byte
	nonce2         [24]byte
	secretKey      *[32]byte
	privateKey     *[32]byte
	publicKey      *[32]byte
	privateKey2    *[64]byte
	publicKey2     *[32]byte
	associatedData []byte // Chacha20poly1305
}

func (r *sc) Key(key []byte) *sc {
	r.key = key

	return r
}
func (r *sc) Key2(key2 [32]byte) *sc {
	r.key2 = key2

	return r
}
func (r *sc) Nonce(nonce []byte) *sc {
	r.nonce = nonce

	return r
}

// Rc4
// key5 1<=x<=256
func (r *sc) Rc4(source []byte) []byte {
	encrypted := make([]byte, len(source))

	cipher, err := rc4.NewCipher(r.key)
	if err != nil {
		panic(err)
	}

	cipher.XORKeyStream(encrypted, source)

	return encrypted
}

// Salsa20
// key5   *[32]byte
// nonce 8 24
func (r *sc) Salsa20(source []byte) []byte {
	encrypted := make([]byte, len(source))

	salsa20.XORKeyStream(encrypted, source, r.nonce, &r.key2)

	return encrypted
}

// Chacha20
// key5   chacha20.KeySize 32
// nonce chacha20.NonceSize 12
//       chacha20.NonceSizeX 24
func (r *sc) Chacha20(source []byte) []byte {
	encrypted := make([]byte, len(source))

	cipher, err := chacha20.NewUnauthenticatedCipher(r.key, r.nonce)
	if err != nil {
		panic(err)
	}

	// cipher.SetCounter(r.counter)
	cipher.XORKeyStream(encrypted, source)

	return encrypted
}
func (r *sc) HChacha20() []byte { // 随机字符串
	key, err := chacha20.HChaCha20(r.key, r.nonce)
	if err != nil {
		panic(err)
	}

	return key
}

// Chacha20poly1305 同 Chacha20
// key5   chacha20poly1305.KeySize 32
// nonce chacha20poly1305.NonceSize 12
//       chacha20poly1305.NonceSizeX 24
// cryptor.NonceSize()
// cryptor.Overhead()
func (r *sc) Chacha20poly1305(source []byte) []byte {
	cryptor, err := chacha20poly1305.New(r.key)
	if err != nil {
		panic(err)
	}

	return cryptor.Seal(nil, r.nonce, source, r.associatedData)
}
func (r *sc) Chacha20poly1305Decrypt(source []byte) []byte {
	cryptor, err := chacha20poly1305.New(r.key)
	if err != nil {
		panic(err)
	}

	decrypted, err := cryptor.Open(nil, r.nonce, source, r.associatedData)
	if err != nil {
		panic(err)
	}

	return decrypted
}

// NaclBox 非对称加密 加密消息比原始消息多 box.Overhead 16
// publicKey *[32]byte
// privateKey  *[32]byte
// nonce [24]byte nonce4
func (r *sc) NaclBox(message []byte) []byte {
	encrypted := make([]byte, len(message)+box.Overhead)

	box.Seal(encrypted, message, &r.nonce2, r.publicKey, r.privateKey)

	return encrypted
}
func (r *sc) NaclBoxDecrypted(source []byte) []byte {
	decrypted := make([]byte, len(source)-box.Overhead)

	_, ok := box.Open(decrypted, source, &r.nonce2, r.publicKey, r.privateKey)
	if !ok {
		panic("Decryption failed")
	}

	return decrypted
}

// NaclSecretBox 对称加密
// key5 [32]byte key4
// nonce [24]byte nonce4
func (r *sc) NaclSecretBox(source []byte) []byte {
	return secretbox.Seal(nil, source, &r.nonce2, &r.key2)
}
func (r *sc) NaclSecretBoxDecrypted(source []byte) []byte {
	decrypted, ok := secretbox.Open(nil, source, &r.nonce2, &r.key2)
	if !ok {
		panic("Decryption failed")
	}

	return decrypted
}

// NaclAuth 消息签名
// secretKey *[32]byte
func (r *sc) NaclAuth(source []byte) [32]byte {
	return *auth.Sum(source, r.secretKey)
}
func (r *sc) NaclAuthVerify(source, mac []byte) bool {
	return auth.Verify(mac[:], source, r.secretKey)
}

// NaclSign 消息签名
// privateKey *[64]byte privateKey2
// publicKey *[32]byte publicKey2
func (r *sc) NaclSign(source []byte) []byte {
	return sign.Sign(nil, source, r.privateKey2)
}
func (r *sc) NaclSignVerify(source, signed []byte) bool {
	message, ok := sign.Open(nil, signed, r.publicKey2)
	if !ok {
		panic("Verify failure")
	}

	return bytes.Equal(message, source)
}
